  ' MMBasic Program Source Crunch Part II v2.4
  ' Hugh Buckle  May 2013
  ' This program takes the output from CRUNCH.BAS and builds the
  ' new source file. See CRUNCH.BAS for details of the work so far.

  ' Input to Part II is:

  ' File numbers:
  '   1 CRUNCHa.DAT file containing parameters passed from CRUNCH.BAS
  '   2 Final output file
  '   3 CMDParm file
  '   4 Reserved word file
  '   5 File of labels and variables found in the source file
  '   6 Temporary file of source with variables marked
  '   7 Temporary file of Subroutine and Function names and Labels
  '   8 File of 1 and 2 word commands
  True=1
  False=Not True
  Testing=False
  GenVarTrace=False
  ParmFile$="CRUNCHa.DAT"
  ShortCmdFile$="RWordS.txt"

  '***********************************************************************
  ' Mainline
  '***********************************************************************

  Print
  Print "Starting Crunch Part II"
  Print "Getting parameters from ";ParmFile$
  GetParms
  Print "Getting list of short reserved words from ";ShortCMDFile$
  GetShortCommands

  '**********************************************************
  'First we need to sort the list of variables and remove duplicates
  'and assign new short names to each.
  '**********************************************************
  Print "Building variable array with replacement names."
  LoadVariables(VariableFile$,MaxVariables)
  BuildVariableArray(MaxVariables)

  'Now process each line in turn, replacing marked variables with
  'their new value.
  Open TempFile$ For input As #6
  Open Ofile$ For Output As #2
  Print "Replacing label, variable & Function/Sub names with short ones."
  PrintMsg("opening input file "+TempFile$+" and output file "+Ofile$)
  Do
    Line Input #6, CLine$
    If testing Then PrintMsg("Input ="+Cline$)
    ReplaceVariables(Cline$)
    If testing Then PrintMsg("Output="+Cline$):PrintMsg("")
    Print #2, Cline$
  Loop Until Eof(#6)

  Print
  Print String$(78,"_")
  Print "Crunching finished. Please test the output file."
  Close #2,#6

  if not Testing then
    Kill VariableFile$
    Kill TempFile$
    option error continue 'Remove the temporary old name / new name file
    kill "CrunchSV.tmp"
    Option Error abort
  Endif
  '***********************************************************************
  ' End of Mainline
  '***********************************************************************
End

Sub LoadVariables(Fname$,MaxEntries)
  '**********************************************************
  'Loads variables, labels and function names into an array,
  'dropping duplicates and sorting into ASCII sequence
  '**********************************************************
  '**********************************************************
  'This creates a sorted array, ignoring duplicates, taking input from a file.
  'This could be done by loading them all in and then sorting and finally
  'removing duplicates, bus since the numbers are likely to be relatively small
  'the slot-in method will probably be just as fast.
  '**********************************************************
  Local EntriesLoaded,i,j
  MaxEntries=MaxEntries+1   'The extra one is for a high-values entry
  Open Fname$ For Input As #7
  Dim Entry$(MaxEntries+1)
  Input #7,Entry$(1)
  Entry$(2)=Chr$(255)
  EntriesLoaded=2
  Do
    Input #7, a$
    For i=1 To EntriesLoaded
      If a$=Entry$(i) Then Exit For            'Ignor a duplicate
      If a$<Entry$(i) Then
        'Shuffle higher ones down the array and slot the new one in here
        For j=EntriesLoaded To i Step -1
          Entry$(j+1)=Entry$(j)
        Next j
        Entry$(i)=a$
        EntriesLoaded=EntriesLoaded+1
        Exit For
      EndIf
    Next i
  Loop Until Eof(#7)
  MaxEntries=EntriesLoaded-1         'This discards the high values entry
  Close #7
End Sub 'LoadVariables

Sub BuildVariableArray(Count)
  '**********************************************************
  'Copies the temporary array to one of the correct size and
  'adds a replacement name for each variable.
  '**********************************************************
  Dim Variable$(MaxVariables)
  For i=1 To MaxVariables
    Variable$(i)=UCase$(Entry$(i))+","+NewVarName$(Entry$(i))
    If testing And GenVarTrace Then Print Variable$(i)
  Next
  Erase Entry$
  If testing Then
    PrintMsg(Str$(MaxVariables)+" Variable names loaded.")
    Open "CrunchSV.tmp" For output As #9
    For i=1 To MaxVariables
      PrintMsg (Variable$(i))
      CommaPos=Instr(1,Variable$(i),",")
      Print #9, Mid$(Variable$(i),CommaPos+1),Left$(Variable$(i),CommaPos-1)
    Next
    Print
    Close #9
  EndIf
End Sub 'BuildVariableArray

Function NewVarName$(OldName$)
  '**********************************************************
  'Assigns a new uppercase short name. Alpha and numeric variables
  'have an independent sequence of one and two character names.
  'AlphaNo1 and NumNo1 are the ASCII value of the first character.
  'AlphaNo2 and NumNo2 are the ASCII value of the second character.
  'The generated numeric variable name is checked against the
  'list of 1 and 2 character reserved words and the next in the
  'series chosen if it is there.
  '**********************************************************
  If Right$(OldName$,1)="$" Then
    'This routine is just for text names
    If AlphaNo2=0 Then
      AlphaNo2=65
    Else
      AlphaNo2=AlphaNo2+1
      If AlphaNo2>90 Then
        If AlphaNo1=0 Then
          AlphaNo1=65
        Else
          AlphaNo1=AlphaNo1+1
        EndIf
        AlphaNo2=65
      EndIf
    EndIf
    If AlphaNo1=0 Then
      NewVarName$=Chr$(AlphaNo2)+"$"
    Else
      NewVarName$=Chr$(Alphano1)+Chr$(AlphaNo2)+"$"
    EndIf
  Else
    'This routine is just for numeric names
    If testing Then printmsg("Generating numeric replacement variable name")
    Do
      If NumNo2=0 Then               'if single char name
        NumNo2=65
      Else
        NumNo2=NumNo2+1
        If NumNo2>90 Then
          If NumNo1=0 Then
            NumNo1=65
          Else
            NumNo1=NumNo1+1
          EndIf
          NumNo2=65
        EndIf
      EndIf
      If NumNo1=0 Then               'Single character name
        NewVarName$=Chr$(NumNo2)
      Else                          '2 character name
        NewVarName$=Chr$(NumNo1)+Chr$(NumNo2)
      EndIf
      If testing Then printmsg( "NewVarName="+Newvarname$)

      'If the generated variable name is in the list of short reserved words
      ' then geneate a new one.
      Found =False
      For iz=1 To ShortCmdCount
        If Newvarname$=ShortCmd$(iz) Then
          Found=true
          Exit For
        EndIf
      Next
    Loop Until Not found
  EndIf
End Function 'NewVarName

Sub ReplaceVariables(Cline$)
  '**********************************************************
  'Looks at each input source line in turn and invokes processing
  'based upon the options selected.
  '**********************************************************
  StartPtr=0
  EOL=False
  Do
    Var$=FindVariable$(Cline$,StartPtr,EndPtr)
    If Var$<>"" Then
      ShortName$=GetShortName$(Var$)
      ReplaceName(Cline$,StartPtr,EndPtr,ShortName$)
    EndIf
  Loop Until Var$=""
End Sub 'ReplaceVariables

Function FindVariable$(Cline$,StartPos,EndPos)
  '**********************************************************
  'Finds the next variable start and end points in the line
  '**********************************************************
  StartPos=StartPos+1
  StartPos=Instr(StartPos,Cline$,VarStartMark$)
  If StartPos=0 Then     'No more variables in this line of code
    FindVariable$=""
  Else
    EndPos=Instr(StartPos+1,Cline$,VarEndMark$)
    FindVariable$=Mid$(Cline$,StartPos+1,EndPos-StartPos-1)
  EndIf
End Function 'FindVariables

Function GetShortName$(Var$)
  '**********************************************************
  'Finds the variable name in the table and returns the short name.
  'Each element in the table contains the long name and short name
  'separated by a comma.
  'The function uses a binary search to find the entry.
  'The size of the table is MaxVariables
  ' BinSearch returns Found = true if the value was found, false if not.
  ' If found, it returns Indx pointing to the matching element.
  '**********************************************************
  Local StartIndx,EndIndx,Indx,Found
  StartIndx=1
  EndIndx=MaxVariables
  Found = False
  Var$=UCase$(Var$)
  Do
    ' Find mid point of the section of the array to search
    Indx = StartIndx + Fix((EndIndx - StartIndx)/2)
    Variable$=Left$(Variable$(Indx),Instr(1,Variable$(Indx),",")-1)
    If Var$ = Variable$ Then
      Found = True
    ElseIf Var$ > Variable$ Then
      StartIndx = Indx + 1
    Else
      EndIndx = Indx - 1
    EndIf
  Loop Until Found Or StartIndx > EndIndx Or EndIndx < StartIndx
  If Not found Then
    Print "Terminal error: Variable "+Var$+" not found."
End
  EndIf

  'The short name immediately follows a comma in the array element
  GetShortName$=Mid$(Variable$(Indx),Instr(1,Variable$(Indx),",")+1)
End Function 'GetShortName$

Sub ReplaceName(Cline$,StartPos,EndPos,ShortName$)
  '**********************************************************
  'Replaces the variable with the new short name.
  'The table contains pairs of values, "old name, new name".
  '**********************************************************
  If StartPos=1 Then
    Cline$=ShortName$+Mid$(Cline$,EndPos+1)
  Else
    Cline$=Left$(Cline$,StartPos-1)+ShortName$+Mid$(Cline$,EndPos+1)
  EndIf

  'Adjust the start position for finding the next variable
  StartPos=StartPos+Len(ShortName$)
End Sub 'ReplaceVariable

Sub GetParms
  '**********************************************************
  'Parameters passed from CRUNCH.BAS
  '  VariableFile$ = unsorted list of variables (inc duplicates)
  '  TempFile$     = Source code with variables marked
  '  Ofile$        = Final output filename
  '  MaxVariables  = number of entries in TempFile$
  '  Var*Mark      = Start and end mark of variables in TempFile$
  '  PauseList     = a swithc that pauses the list at a page full
  '  NoCombineLines= a switch suppressing the creation of multi-statement lines
  '  MaxLineLen    = Max line length when creating multi-statement lines
  '**********************************************************
  Open ParmFile$ For input As #1
  Input #1, VariableFile$,TempFile$,OFile$
  Input #1, MaxVariables,VarStartMark$,VarEndMark$
  Input #1, Pauselist,NoCombineLines,MaxLineLen
  input #1, Testing
  If testing Then
    PrintMsg("Files: Variable="+VariableFile$+" TempSource="+TempFile$+" Output="+OFile$)
    PrintMsg("MaxVariables="+Str$(MaxVariables)+" VarStartMark="+VarStartMark$+" VarEndMark="+VarEndMark$)
    PrintMsg("PauseList="+Str$(Pauselist)+" NoCombineLines="+Str$(NoCombineLines)+" MaxLineLen="+Str$(MaxLineLen))
    PrintMsg("")
  EndIf
  Close #1
End Sub 'GetParms

Sub GetShortCommands
  '**********************************************************
  'This populates an array of 1 and 2 reserved words which
  'must not be used as replacement variable/label/function names.
  '**********************************************************
  If testing Then printmsg("Getting list of short Commands")
  Local i,a$
  Open ShortCmdFile$ For input As #8
  Input #8, ShortCmdCount
  If testing Then printmsg("ShortCmdCount="+Str$(ShortCmdCount))
  Dim ShortCmd$(ShortCmdCount)
  For i=1 To ShortCmdCount
    Input #8, ShortCmd$(i)
    If testing Then a$=a$+ShortCmd$(i)+" "
  Next
  If testing Then
    printmsg(a$)
    PrintMsg("")
  EndIf
  Close #8
End Sub 'GetShortCommands


Sub PrintMsg(Msg$)
  '**********************************************************
  ' Causes the program to pause at the end of each screenful.
  ' Any keystroke causes the program to continue.
  '**********************************************************
  If PauseList Then
    Print Msg$
    w=w+1
    If w>30 Then
      w=0
      Do
        k$=Inkey$
      Loop Until k$<>""
    EndIf
  EndIf
End Sub 'PrintMsg
